/*
 *  Copyright (c) 2022 ZhuHai Jieli Technology Co.,Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "asm/includes.h"
#include "asm/hwi.h"
#include "init.h"

#ifndef CONFIG_BP_DEBUG_ENABLE
// 用于芯片验证
#define _BP_CON(x)                q32DSP(x)->BPCON
#define _BP_BASE(x)                q32DSP(x)->BP0

_WEAK_
void breakpoint_cb(void)
{
    local_irq_disable();

    u8 cpu_id = OS_CPU_ID;

    _BP_CON(cpu_id) |= BIT(31);
    printf("breakpoint : %d", cpu_id);

    local_irq_enable();
}

static void breakpoint_isr(void)
{
    __asm__ volatile
    (
        "    [--sp] = {r15-r0}                    \r\n"
        "    [--sp] = {psr,rets,reti,icfg}       \r\n"
        "                                        \r\n"
        "   call %0                             \r\n" /* Dynamic stack */
        "                                        \r\n"
        "    {psr,rets,reti,icfg} = [sp++]       \r\n"
        "    {r15-r0} = [sp++]                   \r\n"
        "                                        \r\n"
        "   rte                                 \r\n" /* Execute a return from interrupt instruction */
        ::"i"(breakpoint_cb)
    ) ;
}

static int breakpoint_init(void)
{
    request_irq(IRQ_EMUEXCPT_IDX, 3, breakpoint_isr, 0);
    request_irq(IRQ_EMUEXCPT_IDX, 3, breakpoint_isr, 1);

    return 0;
}
early_initcall(breakpoint_init);

void breakpoint_reg(u8 index, u32 addr)
{
    u32 *bpcon;

    printf("BP[%d] : %x", index, addr);

    index = (index > 3) ? index + 1 : index;

    for (u8 i = 0; i < OS_CPU_NUM; i++) {

        bpcon = (u32 *)&_BP_BASE(i);

        bpcon[index] = addr | BIT(0);       // BIT(0):en
    }
}

void breakpoint_test(void)
{
    void timer(void *p);
    breakpoint_reg(0, (u32)timer);
}
#else

// ===========================================================//
//               Test BreakPoint In Main System               //
// ===========================================================//
#define SUPPORT_BP_DEBUG_TEST_DEMO 1

#if (SUPPORT_BP_DEBUG_TEST_DEMO == 1)
#include "timer.h"
// 5s
#define TIMER2_PERIOD         5000
#define TIMER3_PERIOD         4000
#define TIMER4_PERIOD         3000
#define TIMER5_PERIOD         2000

#define TIMER0_PERIOD         3000

___interrupt
static int timer0_isr(void)
{
    JL_TIMER0->CON |= BIT(14);

    printf("%s:%d", __func__, __LINE__);

    return 0;
}

static void timer_hook_handle(u32 value)
{
    int tmp;
    __asm__ volatile("%0 = icfg":"=r"(tmp));
    printf("icfg = 0x%x, value = 0x%x", tmp, value);
}

___interrupt
static void timer2_isr(void)
{
    JL_TIMER2->CON |= BIT(14);
    timer_hook_handle(2);

    printf("%s:%d", __func__, __LINE__);
}

___interrupt
static void timer3_isr(void)
{
    JL_TIMER3->CON |= BIT(14);
    timer_hook_handle(3);
    printf("%s:%d", __func__, __LINE__);
}

// insert
static int sys_timer_handle_2_insert_1(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
{
    static int cnt = 0;
    printf("arg1 = 0x%x", arg1);
    printf("arg2 = 0x%x", arg2);
    printf("arg3 = 0x%x", arg3);
    printf("arg4 = 0x%x", arg4);
    printf("arg5 = 0x%x", arg5);
    printf("arg6 = 0x%x", arg6);
    cnt++;

    return arg1 + arg2 + arg3 + arg4 + arg5 - arg6 + cnt;
}

static int sys_timer_handle_2_hook(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
{
    int ret;
    static int cnt = 0;
    printf("arg1 = 0x%x", arg1);
    printf("arg2 = 0x%x", arg2);
    printf("arg3 = 0x%x", arg3);
    printf("arg4 = 0x%x", arg4);
    printf("arg5 = 0x%x", arg5);
    printf("arg6 = 0x%x", arg6);
    cnt++;

    ret = sys_timer_handle_2_insert_1(1, 2, 3, 4, 5, 6);
    printf("sys_timer_handle_2_insert_1 return 0x%x\n", ret);

    return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + cnt;
}

static void sys_timer_handle_3(void *priv)
{
    putchar('\n');
    putchar('s');
    putchar('y');
    putchar('s');
    putchar('1');
    sys_timer_handle_2_hook(2, 3, 4, 5, 6, 7);
    putchar('\n');
}

// skip
static void sys_timer_handle_1(void *priv)
{
    putchar('\n');
    putchar('s');
    putchar('y');
    putchar('s');
    putchar('1');
    sys_timer_handle_2_hook(2, 3, 4, 5, 6, 7);
    putchar('\n');
}

// insert
static void sys_timer_handle_2(void *priv)
{
    int ret;
    putchar('\n');
    putchar('0');
    putchar('1');
    putchar('2');
    putchar('3');
    putchar('4');
    putchar('5');
    putchar('6');
    putchar('7');
    putchar('8');
    putchar('9');
    putchar('A');
    putchar('B');
    ret = sys_timer_handle_2_hook(1, 2, 3, 4, 5, 6);
    printf("sys_timer_handle_2_hook return 0x%x\n", ret);
    putchar('C');
    putchar('D');
    putchar('E');
    putchar('F');
    putchar('0');
    putchar('1');
    putchar('2');
    putchar('3');
    putchar('4');
    putchar('5');
    putchar('6');
    putchar('7');
    putchar('8');
    putchar('9');
    putchar('A');
    putchar('B');
    putchar('C');
    putchar('D');
    putchar('E');
    putchar('F');
    ret = sys_timer_handle_2_insert_1(1, 2, 3, 4, 5, 6);
    printf("sys_timer_handle_2_insert_1 return 0x%x\n", ret);
    putchar('\n');
}

static const u32 timer_div[] = {
    1,         /* 0000 */
    4,         /* 0001 */
    16,        /* 0010 */
    64,        /* 0011 */
    2,         /* 0100 */
    8,         /* 0101 */
    32,        /* 0110 */
    128,       /* 0111 */
    256,       /* 1000 */
    4 * 256,   /* 1001 */
    16 * 256,  /* 1010 */
    64 * 256,  /* 1011 */
    2 * 256,   /* 1100 */
    8 * 256,   /* 1101 */
    32 * 256,  /* 1110 */
    128 * 256, /* 1111 */
};

#define APP_TIMER_CLK           clk_get("timer")
#define MAX_TIME_CNT            0x7fff
#define MIN_TIME_CNT            0x100

static void ___breakpoint_test_init(void)
{
    printf("%s:%d", __func__, __LINE__);
    u8 index;
    u32 prd_cnt;
    // timer2:
    for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
        prd_cnt = TIMER2_PERIOD * (APP_TIMER_CLK / 1000) / timer_div[index];
        if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
            break;
        }
    }

    request_irq(IRQ_TIME2_IDX, 1, timer2_isr, 0);

    JL_TIMER2->CNT = 0;
    JL_TIMER2->PRD = prd_cnt; // 2ms
    JL_TIMER2->CON = BIT(14);
    JL_TIMER2->CON = (5 << 10) | (index << 4) | BIT(0); // std_12M

    // timer3:
    for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
        prd_cnt = TIMER3_PERIOD * (APP_TIMER_CLK / 1000) / timer_div[index];
        if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
            break;
        }
    }
    request_irq(IRQ_TIME3_IDX, 1, timer3_isr, 0);

    JL_TIMER3->CNT = 0;
    JL_TIMER3->PRD = prd_cnt; // 2ms
    JL_TIMER3->CON = BIT(14);
    JL_TIMER3->CON = (5 << 10) | (index << 4) | BIT(0); // std_12M

    // timer4:
    for (index = 0; index < (sizeof(timer_div) / sizeof(timer_div[0])); index++) {
        prd_cnt = TIMER0_PERIOD * (APP_TIMER_CLK / 1000) / timer_div[index];
        if (prd_cnt > MIN_TIME_CNT && prd_cnt < MAX_TIME_CNT) {
            break;
        }
    }
    request_irq(IRQ_TIME0_IDX, 1, timer0_isr, 0);

    JL_TIMER0->CNT = 0;
    JL_TIMER0->PRD = prd_cnt; // 2ms
    JL_TIMER0->CON = BIT(14);
    JL_TIMER0->CON = (5 << 10) | (index << 4) | BIT(0); // std_12M

    sys_timer_add(NULL, sys_timer_handle_1, 3000); // 3s
}

static void __timer_test_init(void)
{
    ___breakpoint_test_init();
}

void app_extension_test(void);
// 建议添加位置
/* lib/system/init/init.c 的 os_start();前 */
void breakpoint_test(void)
{
    ___breakpoint_test_init();
}
#endif /* // Test BreakPoint In Main System */
#endif /* #ifndef CONFIG_BP_DEBUG_ENABLE */
